#!/usr/bin/env python
# -*- coding: utf-8 -*-
# output. data from DB.
import re
import locale
import sys
import codecs
#import EkikaraDb
import LoopLine
import TimetableExporter
import OudiaFormatError
from OudiaFormatDefs import *
import TrainGrade
import DispProp


class OudiaFormat(TimetableExporter.TimetableExporter):
    u"""OuDia oud形式ファイルを出力

    oud形式
    テキストファイル。cp932。CRLF。
    上り下りが1つのファイルに入る。
    セクションは末尾が'.'で終わる行で始まり、'.'だけの行で終わる
    各行はラベルと値となり、=が区切りとなる。
    行連結機能は無い。注記項目等で改行したい場合は '\n' という文字列を入れる
    駅は上り下り共用。セクション内のラベルの一部がのぼり用下り用に対応している
    oudia時刻形式:
    military time
    hhmm ':'は無し 時刻10の位のみ0サプレスするため、 05:00 は 500 となる。(0:00〜0:59 は 000〜059 ?)
    (None) 無し。
    n;hhnn nは1の時時刻。キホンは発時刻。 hhnn/hhnn の場合、着/発。  hhnn/ 着のみ。
    nが2の時は通過(レ) nが3の時は経由無し(||)
    列車種別はマスタ定義し、各列車にはその種別IDが入る。※注意:コードは定義順となる。つまり、出力時までは決定できない。
    駅名の出力範囲がリストで指定されればそれに従う。通常は下り・上りの情報から自動生成する。
    ekiList = [ eki, eki, eki... ]
    eki = { 'name':<<station name>>, 'down':ekiAttr, 'up':ekiAttr }
    ekiAttr = { 'display_arrival':<<True/False>>, 'display_departure':<<True/False>>}

    駅リスト外部指定:
    <<駅名>>\t<<attrs>>

    <<attrs>> ::= dad | da | dd | uad | ua | ud | \t <<attrs>>
    """
    def __init__(self,
                 db,
                 timetable_id,  # 出力対象の時刻表。piarがあるかどうかチェックし、ある場合はpairも出力する。
                 logger,        # ログ出力用オブジェクト
                 argv,          # oudファイルのコメントに書き込むこのスクリプトのコマンドライン
                 encode=None,   # oudファイルのエンコード。None指定時はデフォルトの'CP932'(oudファイルの仕様)
                 newline=None,  # oudファイルの改行コード。None指定時はデフォルトのCRLF(oudファイルの仕様)
                 status_option=False,   # コンソールにステータスメッセージを出力するかどうか
                 console_encode=locale.getpreferredencoding(),  # ステータスメッセージ出力時のエンコード
                 isCheckOrder=True):    # 駅名マッチング時、下りは時刻昇順、上りは時刻降順になって無い時は掲載しない
        TimetableExporter.TimetableExporter.__init__(self, db, timetable_id, logger, argv, encode, newline,
                                                     status_option=status_option,
                                                     console_encode=console_encode)
        # override self.encode proccessing
        if not self.encode:
            self.encode = 'CP932'       # dia data output encode
        if not self.newline:
            self.newline = u"\r\n"      # CRLF
        self.grade = TrainGrade.TrainGrade(logger)
        self.dispProp = DispProp.DispProp(logger)
        #pair-id to pair objects
        pairId = self.db.pairTimetable(timetable_id)
        self.pair = {'down': None, 'up': None}
        if pairId['down'] != None:
            self.pair['down'] = LoopLine.TimetableLoopLine(self.db, pairId['down'], logger, isCheckOrder)
        if pairId['up'] != None:
            self.pair['up'] = LoopLine.TimetableLoopLine(self.db, pairId['up'], logger, isCheckOrder)
        if pairId['down'] == None and pairId['up'] == None:
            # 上下不明の時は下りに入れる
            self.pair['down'] = LoopLine.TimetableLoopLine(self.db, timetable_id, logger, isCheckOrder)
        self.stationListFile = None
        self.stationListEncode = 'CP932'

    def putHeader(self):
        pass

    def _importStationList(self,
                           stationListFile,
                           stationListEncode,
                           ):
        u"""ファイルより駅名リストを取り込みoudaia_ekiテーブルにセット

        読み込み時はnewlineに頓着しないっぽい。
        """
        reComment = re.compile("^#")
        reArrive = re.compile("[Aa]")
        reDeparture = re.compile("[Dd]")
        try:
            fin = codecs.open(stationListFile, "rb", stationListEncode)
        except IOError:
            raise OudiaFormatError.OudiaFormatError(u"can not open station list file:%s" % (stationListFile))
        self.db.clearOuDiaEki()
        for line in fin:
            line = line.strip()
            mComment = reComment.search(line)
            if mComment:
                continue
            (stationName, downArriveDeparture, upArriveDeparture) = line.split(u"\t")
            downArrival = 0
            downDeparture = 0
            upArrival = 0
            upDeparture = 0
            if reArrive.search(downArriveDeparture):
                downArrival = 1
            if reDeparture.search(downArriveDeparture):
                downDeparture = 1
            if reArrive.search(upArriveDeparture):
                upArrival = 1
            if reDeparture.search(upArriveDeparture):
                upDeparture = 1
            if downArrival == 0 and downDeparture == 0:
                downDeparture = 1
            if upArrival == 0 and upDeparture == 0:
                upDeparture = 1
            self.db.addOuDiaEki(stationName, downArrival, downDeparture, upArrival, upDeparture, byCommit=False)
        fin.close()
        self.db.conn.commit()

    def setStationList(self, filename, encode='CP932'):
        self.stationListFile = filename
        self.stationListEncode = encode

    def putEkiList(self, fout):
        u"""駅名リストを出力する。

        """
        if self.stationListFile:
            self._importStationList(self.stationListFile, self.stationListEncode)
        else:
            self.db.clearOuDiaEki()
            if self.pair['down']:
                self.pair['down'].setToOuDiaEki()     # 要LOOP対応:merge
            elif self.pair['up']:
                self.pair['up'].setToOuDiaEki()     # 要LOOP対応:merge
            else:
                assert False, u"nothing station list"
        self._statusMsg(u"駅名:")
        for eki in self.db.listOuDiaEki(u"down"):      # 下り基準
            self._writeln(fout, u"Eki" + OUD_BLOCK)
            self._writeln(fout, u"Ekimei=%s" % (eki['eki_name']))
            self._statusMsg(u"%s" % (eki['eki_name']))
            dispType = u"Ekijikokukeisiki="
            if (eki['down_arrival'] or eki['up_arrival']) and (eki['down_departure'] or eki['up_departure']):
                dispType += u"Jikokukeisiki_Hatsuchaku"
                self._statusMsg(u"ad")
            elif eki['down_arrival'] or eki['up_arrival']:
                dispType += u"Jikokukeisiki_KudariChaku"
                self._statusMsg(u"a")
            elif eki['down_departure'] or eki['up_departure']:
                dispType += u"Jikokukeisiki_Hatsu"
                self._statusMsg(u"d")
            else:
                dispType += u"Jikokukeisiki_Hatsu"      # if unkown then display departure.
                self._statusMsg(u"d")
            self._writeln(fout, dispType)
            self._writeln(fout, u"Ekikibo=Ekikibo_Ippan")
            self._writeln(fout, OUD_BLOCKEND)
            self._statusMsg(u",")
        self._statusMsg(u"\n")

    def cnvTimeFmt(self, element):
        u"""えきから時刻表の時刻形式(hh:nn)をOuDiaの時刻形式 hhnn に変換

        hourは0サプレスするが、minuteは0サプレスしない。 "00:00" → "000", "0106" → "106"
        """
        reTime = re.compile(u"(\d+):(\d+)")
        reMatch = reTime.match(element.strip())
        if reMatch:
            return u"%d%02d" % (int(reMatch.group(1)), int(reMatch.group(2)))
        else:
            return u""

    def toEkijikoku(self, arrival, departure):
        u"""OuDiaのEkijikoku形式に変換する。少なくとも列車運行範囲にある(駅名一致後)

        通過;到着時刻無し and 発時刻無し
        発時刻
        着時刻
        """
        if arrival == None:
            arrival = u""
        if departure == None:
            departure = u""
        if arrival == u"" and departure == u"":
            return u""
        elif (arrival == u"レ" and departure == u"レ") or (arrival == u"" and departure == u"レ") or (arrival == u"レ" and  departure == u""):
            return u"2"
        elif arrival == u"" and departure != u"":
            return u"1;%s" % (self.cnvTimeFmt(departure))
        else:
            return u"1;%s/%s" % (self.cnvTimeFmt(arrival), self.cnvTimeFmt(departure))

    def getRailwayLineName(self):
        attr = []
        if self.pair['down'] != None:
            attr = self.pair['down'].getAttr()
        elif self.pair['up'] != None:
            attr = self.pair['up'].getAttr()
        else:
            self.logger.error("pair up and down is None")
            attr['company'] = u""
            attr['railway_line'] = u"unkonwn"
        return attr['company'] + attr['railway_line']

    def formatNote(self, note):
        if note == None:
            note = u""
        note = note.strip()
        note = note.replace(u" ", u"")
        note = note.replace(u"　", u"")       # 全角空白
        note = note.replace(u"\n", u"")
        return note

    def otherLine(self, timeList):
        u"""経由しない(||)処理。値としてu"3"をセット
        """
        start_index = -1
        for i, v in enumerate(timeList):
            if v != u"":
                start_index = i
                break
        end_index = -1
        for i in reversed(range(0, len(timeList) - 1)):
            if timeList[i] != u"":
                end_index = i
                break
        for i in range(start_index, end_index):
            if timeList[i] == u"":
                timeList[i] = u"3"
        return timeList

    def putTrains(self, fout, direction):
        if direction == u"down":
            directionBlockName = u"Kudari"
            timetable = self.pair['down']
        elif direction == u"up":
            directionBlockName = u"Nobori"
            timetable = self.pair['up']
        else:
            pass        # ...結果として中身が空のBLOCKを返す
        self._writeln(fout, directionBlockName + OUD_BLOCK)  # 中身が空でも出力する必要がある。
        if timetable != None:
            #downAttr = timetable.getAttr()
            trainList = []
            for t in timetable.trainList():
                trainList.append({'train_number':      t['train_number'],
                                  'train_name':        t['train_name'],
                                  'train_type':        t['train_type'],
                                  'note':              t['note'],
                                  'gou':               t['gou'],
                                  'url':               t['url'],
                                  'train_type_prefix': t['train_type_prefix'],
                                  'date':              t['date'], })
            self.logger.debug(self.pp.pformat(trainList))
            #for train in timetable.trainList():
            self._statusMsg(u"direction:%s\n" % (direction))
            for train in trainList:
                if timetable.prepareEkiTimeCursor(train['train_number'], train['url'], direction):
                    self._writeln(fout, u"Ressya" + OUD_BLOCK)
                    self._writeln(fout, u"Houkou=" + directionBlockName)
                    trainGradePrefix = u""
                    if train['train_type_prefix']:
                        trainGradePrefix = train['train_type_prefix'].strip()
                    trainGrade = u""
                    if train['train_type']:
                        trainGrade = train['train_type'].strip()
                    self._writeln(fout, u"Syubetsu=%d" % (self.grade.getCode(trainGrade)))
                    self._writeln(fout, u"Ressyabangou=%s" % (train['train_number']))
                    trainName = u""
                    if train['train_name']:
                        trainName = trainGradePrefix + trainGrade + u" " + train['train_name']
                    else:
                        # 私鉄無料特急等、名前の無い特急の処理
                        # 只の「快速」,「急行」,「特急」には興味有りません。
                        if trainGradePrefix:
                            trainName = trainGradePrefix + trainGrade       # 列車種別略称設定がある時
                        elif trainGrade:
                            trainName = trainGrade  # 列車種別略称設定が無く、列車名が空の時
                    self._statusMsg(u"%s," % (train['train_number']))
                    self._writeln(fout, u"Ressyamei=%s" % (trainName))
                    if train['gou'] != None:
                        self._writeln(fout, u"Gousuu=" + train['gou'])
                    trainJikoku = {}
                    for eki in timetable.ekiTimeCursor(train['train_number'], train['url']):
                        if eki['eki_order'] != None:
                            # 要LOOP対応:単純にtrainJikoku[≪駅名≫]としているが、
                            #            同一駅名が複数出てくるので指定の駅名が
                            #            「どちらの」駅名であるか分かるようにしておく必要がある。
                            trainJikoku[eki['eki_order']] = self.toEkijikoku(eki['train_arrival'],
                                                                            eki['train_departure'])
                        else:
                            trainJikoku[eki['station_order']] = u""    # 当該列車の運行範囲外 要LOOP対応。同上
                    ekiJikoku = []
                    for eki in self.db.listOuDiaEki(direction):
                        if eki['eki_order'] in trainJikoku:  # 要LOOP対応。単純に駅名ではマッチングできない。
                            ekiJikoku.append(trainJikoku[eki['eki_order']])       # 要LOOP対応
                        else:
                            ekiJikoku.append(u"")
                    ekiJikoku = self.otherLine(ekiJikoku)   # 経由無し||処理
                    self._writeln(fout, u"EkiJikoku=" + u",".join(ekiJikoku))  # リスト生成
                    trainNote = self.formatNote(train['note'])
                    trainDate = self.formatNote(train['date'])
                    if trainNote or trainDate:
                        self._writeln(fout, u"Bikou=" + trainDate + trainNote)
                    self._writeln(fout, OUD_BLOCKEND)
            self._statusMsg(u"\n")
        self._writeln(fout, OUD_BLOCKEND)       # kudari block

    def putDia(self, fout):
        u""" set trains

        下り、上りとも、時間順(のぼりは駅順と逆になる) ekilistに従う
        →ekilistに従って値を取り出す(いまのところ駅名の重複は無いので)
        """
        attr = []
        # get for title from attr
        if self.pair['down'] != None:
            attr = self.pair['down'].getAttr()
        elif self.pair['up'] != None:
            attr = self.pair['up'].getAttr()
        else:
            self.logger.error("pair up and down is None")
            attr['company'] = u""
            attr['railway_line'] = u"unkonwn"
            attr['day'] = u""
        diaName = u"%s %s(%s" % (attr['company'], attr['railway_line'], attr['day'])
        self._writeln(fout, u"Dia" + OUD_BLOCK)
        self._writeln(fout, u"DiaName=%s" % (diaName))
        self.db.clearTrueTrainStop()    # work table true_train_stop をクリア
        self.putTrains(fout, u"down")
        self.putTrains(fout, u"up")
        self._writeln(fout, OUD_BLOCKEND)       # dia block

    def putRosen(self, fout):
        self._writeln(fout, u"Rosen" + OUD_BLOCK)
        self._writeln(fout, u"Rosenmei=" + self.getRailwayLineName())
        self._statusMsg(u"路線名:%s\n" % (self.getRailwayLineName()))
        self.putEkiList(fout)        # eki
        for line in self.grade.gradeList:     # line style
            self._writeln(fout, line)
        self.putDia(fout)
        self._writeln(fout, u"KitenJikoku=400")
        self._writeln(fout, u"DiagramDgrYZahyouKyoriDefault=60")
        self._writeln(fout, u"Comment=" + u" ".join(self.argv))
        self._writeln(fout, OUD_BLOCKEND)        # rosen block end

    def put(self, fout):
        u"""OuDiaファイル形式の文字列を生成

        """
        self._writeln(fout, u"FileType=OuDia.6")      # header
        self.putRosen(fout)
        for line in self.dispProp.dispPropList:
            self._writeln(fout, line)
        self._writeln(fout, u"FileTypeAppComment=GetEkikara OuDiaFmt Ver.0.5 (OuDia Ver. 1.00.01)")
